package com.unknownman.block.service.impl;

import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.github.pagehelper.PageHelper;
import com.lianziyou.chain.common.api.CommonPage;
import com.lianziyou.chain.common.utils.ConvertContractValue;
import com.unknownman.block.config.SwapTokenConfig;
import com.unknownman.block.constants.ChainContractConstants;
import com.unknownman.block.entity.TContractConfig;
import com.unknownman.block.entity.TContractLogs;
import com.unknownman.block.mapper.TContractLogsMapper;
import com.unknownman.block.mapper.TTokenSwapRecord;
import com.unknownman.block.service.*;
import lombok.extern.slf4j.Slf4j;
import org.cloud.utils.CollectionUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.stereotype.Service;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.DefaultTransactionDefinition;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
import org.web3j.abi.datatypes.Address;
import org.web3j.utils.Numeric;

import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.List;
import java.util.stream.Collectors;

/**
 * <p>
 * 转账记录日志表 服务实现类
 * </p>
 *
 * @author xhy
 * @since 2021-10-19
 */
@Service
@Slf4j
public class TContractLogsServiceImpl extends ServiceImpl<TContractLogsMapper, TContractLogs> implements TContractLogsService {

    @Autowired
    private TContractConfigService contractConfigService;
    @Autowired
    private TTokenErc20Service tokenErc20Service;
    @Autowired
    private TNftService nftService;
    @Autowired
    private DataSourceTransactionManager transactionManager;

    @Override
    public CommonPage<TContractLogs> findListByPage(Integer page, Integer limit, String orderBy, QueryWrapper<TContractLogs> queryWrapper) {
        if (StringUtils.isEmpty(orderBy)) {
            PageHelper.startPage(page, limit);
        } else {
            PageHelper.startPage(page, limit, orderBy);
        }
        List<TContractLogs> list = baseMapper.selectList(queryWrapper);
        return CommonPage.restPage(list);
    }

    @Override
    public boolean existsByHashAndIndex(String hash, Long index) {
        return getBaseMapper().countByHashAndIndex(hash, index) > 0;
    }

    @Override
    public void processUntreatedTokenTransferLogs() {
        List<TContractLogs> logsList = getBaseMapper().selectUntreatedTokenTransferLogs(50);
        if (CollectionUtil.single().isEmpty(logsList)) {
            return;
        }
        List<TContractConfig> contractConfigList = contractConfigService.getListByStatus(null);
        for (TContractLogs contractLog : logsList) {
            TContractConfig contractConfig = contractConfigList.stream()
                .filter(_contractConfig -> _contractConfig.getContractConfigId().equals(contractLog.getContractConfigId()))
                .collect(Collectors.toList()).get(0);
            if (contractConfig.getContractType() == ChainContractConstants.ContractTypeConstants.ERC20.getType()) {  //代币转账
                processLog(contractLog, ChainContractConstants.ContractTypeConstants.ERC20, contractConfig);
            } else if (contractConfig.getContractType() == ChainContractConstants.ContractTypeConstants.ERC721.getType()) {   //NFT转移
                processLog(contractLog, ChainContractConstants.ContractTypeConstants.ERC721, contractConfig);
            }
        }
    }


    @Override
    public void processSwapTokenTransferLogs() {
        List<TContractConfig> contractConfigList = contractConfigService.getListByStatus(null);
        for (TContractConfig contractConfig : contractConfigList) {
            if (ChainContractConstants.ContractTypeConstants.ERC20.getType() != contractConfig.getContractType()) {
                continue;
            }

            if (StringUtils.isEmpty(contractConfig.getExpand1())) {
                continue;
            }

            SwapTokenConfig swapTokenConfig = JSON.parseObject(contractConfig.getExpand1(), SwapTokenConfig.class);

            if (!(swapTokenConfig.getToken0().getIsCoinBase() || swapTokenConfig.getToken1().getIsCoinBase())) {
                log.error("{}未正确的设置法币（稳定币），请检查！{}", contractConfig.getContractAddress(), contractConfig.getExpand1());
                continue;
            }
            if (swapTokenConfig.getToken0().getIsCoinBase() && swapTokenConfig.getToken1().getIsCoinBase()) {
                log.error("{}未正确的设置法币（稳定币），请检查！{}", contractConfig.getContractAddress(), contractConfig.getExpand1());
                continue;
            }
            List<TContractLogs> logsList = getBaseMapper().selectSwapTokenTransferLogs(50, contractConfig.getContractAddress());
            for (TContractLogs contractLog : logsList) {
                this.processSingleSwapTokenTransferLogs(contractLog, contractConfig);
            }
        }


    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateStatusSuccess(Long id) {
        int i = getBaseMapper().updateStatusSuccess(id);
        Assert.isTrue(i > 0, "更新log状态为success失败");
    }

    @Autowired
    TTokenSwapRecordService tTokenSwapRecordService;

    @Transactional(rollbackFor = Exception.class)
    public void processSingleSwapTokenTransferLogs(TContractLogs contractLogs, TContractConfig tContractConfig) {
        TTokenSwapRecord tTokenSwapRecord = new TTokenSwapRecord();
        tTokenSwapRecord.setContractConfigId(tContractConfig.getContractConfigId());
        tTokenSwapRecord.setPairAddress(tContractConfig.getContractAddress());
        tTokenSwapRecord.setTransactionHash(contractLogs.getTransHash());
        tTokenSwapRecord.setBlockNumber(contractLogs.getIndexForBlock());
        tTokenSwapRecord.setBlockTimestamp(contractLogs.getBlockTimestamp());
        tTokenSwapRecord.setFromAddress(new Address(contractLogs.getSecondTopic()).toString());
        tTokenSwapRecord.setToAddress(new Address(contractLogs.getThirdTopic()).toString());
        tTokenSwapRecord.setCreateBy("system");

        SwapTokenConfig swapTokenConfig = JSON.parseObject(tContractConfig.getExpand1(), SwapTokenConfig.class);

        List<String> list = ConvertContractValue.convertDataToValueList(contractLogs.getLogData());

        BigInteger bigAmount0In = Numeric.decodeQuantity(list.get(0));
        BigDecimal amount0In = ConvertContractValue.bigIntegerToBigDecimal(bigAmount0In, swapTokenConfig.getToken0().getDecimals());

        BigInteger bigAmount1In = Numeric.decodeQuantity(list.get(1));
        BigDecimal amount1In = ConvertContractValue.bigIntegerToBigDecimal(bigAmount1In, swapTokenConfig.getToken1().getDecimals());

        BigInteger bigAmount0out = Numeric.decodeQuantity(list.get(2));
        BigDecimal amount0Out = ConvertContractValue.bigIntegerToBigDecimal(bigAmount0out, swapTokenConfig.getToken0().getDecimals());

        BigInteger bigAmount1out = Numeric.decodeQuantity(list.get(3));
        BigDecimal amount1Out = ConvertContractValue.bigIntegerToBigDecimal(bigAmount1out, swapTokenConfig.getToken1().getDecimals());

        if (swapTokenConfig.getToken0().getIsCoinBase()) { // token0时法币的处理
            if (amount0In.compareTo(BigDecimal.ZERO) > 0) {  // token0的值大于零，那么就是买入
                tTokenSwapRecord.setBuyOrSale(10);
                tTokenSwapRecord.setAmountIn(amount0In);  // 对于路由的出金，对于用户来说就是入金
                tTokenSwapRecord.setAmountInSymbol(swapTokenConfig.getToken0().getSymbol());
                tTokenSwapRecord.setAmountOut(amount1Out);  // 对于路由的入金，对于用户来出就是出金
                tTokenSwapRecord.setAmountOutSymbol(swapTokenConfig.getToken1().getSymbol());
                tTokenSwapRecordService.getBaseMapper().insert(tTokenSwapRecord);
            }

            if (amount1In.compareTo(BigDecimal.ZERO) > 0) { // token1的值大于零，那么就是卖出
                tTokenSwapRecord.setBuyOrSale(20);
                tTokenSwapRecord.setAmountIn(amount0Out);
                tTokenSwapRecord.setAmountInSymbol(swapTokenConfig.getToken0().getSymbol());
                tTokenSwapRecord.setAmountOut(amount1In);
                tTokenSwapRecord.setAmountOutSymbol(swapTokenConfig.getToken1().getSymbol());
                tTokenSwapRecordService.getBaseMapper().insert(tTokenSwapRecord);
            }
        } else if (swapTokenConfig.getToken1().getIsCoinBase()) { // token1时法币的处理

            if (amount0In.compareTo(BigDecimal.ZERO) > 0) { // token0的值大于零，那么就是卖出
                tTokenSwapRecord.setBuyOrSale(20);
                tTokenSwapRecord.setAmountIn(amount1Out);
                tTokenSwapRecord.setAmountInSymbol(swapTokenConfig.getToken1().getSymbol());
                tTokenSwapRecord.setAmountOut(amount0In);
                tTokenSwapRecord.setAmountOutSymbol(swapTokenConfig.getToken0().getSymbol());
                tTokenSwapRecordService.getBaseMapper().insert(tTokenSwapRecord);
            }

            if (amount1In.compareTo(BigDecimal.ZERO) > 0) { // token1的值大于零，那么就是买入
                tTokenSwapRecord.setBuyOrSale(10);
                tTokenSwapRecord.setAmountIn(amount1In);
                tTokenSwapRecord.setAmountInSymbol(swapTokenConfig.getToken1().getSymbol());
                tTokenSwapRecord.setAmountOut(amount0Out);
                tTokenSwapRecord.setAmountOutSymbol(swapTokenConfig.getToken0().getSymbol());
                tTokenSwapRecordService.getBaseMapper().insert(tTokenSwapRecord);
            }
        }

        int i = getBaseMapper().updateStatusSuccess(contractLogs.getContractLogId());
        Assert.isTrue(i > 0, "log处理状态更新失败");


    }

    private void processLog(TContractLogs contractLog, ChainContractConstants.ContractTypeConstants contractType,
        TContractConfig contractConfig) {
        boolean saveLogSuccess = false;
        DefaultTransactionDefinition def = new DefaultTransactionDefinition();
        def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);
        TransactionStatus status = transactionManager.getTransaction(def);
        try {
            if (contractType.getType() == ChainContractConstants.ContractTypeConstants.ERC20.getType()) {  //代币转账
                saveLogSuccess = tokenErc20Service.saveByLog(contractLog, contractConfig);
            } else if (contractType.getType() == ChainContractConstants.ContractTypeConstants.ERC721.getType()) {   //NFT转移
                saveLogSuccess = nftService.saveByLog(contractLog);
            }
            if (saveLogSuccess) {
                int i = getBaseMapper().updateStatusSuccess(contractLog.getContractLogId());
                Assert.isTrue(i > 0, "log处理状态更新失败");
            }
            transactionManager.commit(status);
        } catch (Exception e) {
            transactionManager.rollback(status);
            e.printStackTrace();
        }

    }
}
